package com.xzj.mybatis.core;

import com.xzj.mybatis.annotation.*;
import com.xzj.mybatis.utils.JdbcTemplate;
import com.xzj.mybatis.utils.MapperUtil;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: SpringDemo
 * @Package: com.xzj.mybatis.core
 * @ClassName: MapperInvocationHandler
 * @Author: xzj
 * @Description:
 * @Date: 2022/7/22 15:27
 * @Version: 1.0
 */
public class MapperInvocationHandler implements InvocationHandler {

    private Class<?> clz;
    private final JdbcTemplate jdbcTemplate;

    public <T> MapperInvocationHandler(Class<T> type, JdbcTemplate jdbcTemplate) {
        this.clz = type;
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取父接口中的泛型，找到对应的PO
        Class<? extends Type> aClass = (Class) ((ParameterizedType) clz.getGenericInterfaces()[0]).getActualTypeArguments()[0];
        TableName tableNameAnnotation = aClass.getAnnotation(TableName.class);
        String tableName = tableNameAnnotation.value();
        if ("insert".equals(method.getName())) {
            //预编译SQL
            String psql = "insert into %s(%s) values %s";
            Object id = ddlMybatisPlus(psql, tableName, aClass, args[0]);
            Field[] declaredFields = args[0].getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                TableId tableId = declaredField.getAnnotation(TableId.class);
                if (tableId != null) {
                    declaredField.setAccessible(true);
                    declaredField.set(args[0], id);
                    break;
                }
            }
            return 1;
        }
        if ("insertBatch".equals(method.getName())) {
            //预编译SQL
            String psql = "insert into %s(%s) values %s";
            return ddlsMybatisPlus(psql, tableName, aClass, args[0]);
        }
        if ("selectById".equals(method.getName())) {
            //预编译SQL
            String psql = "select * from %s where 1=1 %s";
            return dqlMybatisPlus(psql, tableName, aClass, args[0]);
        }


        //其他方法
        //1.从方法上面拿sql 这里的sql 语法是#{}
        Insert insert = method.getAnnotation(Insert.class);
        if (insert != null) {
            Integer result = ddlMybatis(method, args, aClass, insert.value());
            if (result != null) {
                return result;
            }
        }
        Update update = method.getAnnotation(Update.class);
        if (update != null) {
            Integer result = ddlMybatis(method, args, aClass, update.value());
            if (result != null) {
                return result;
            }
        }
        Delete delete = method.getAnnotation(Delete.class);
        if (delete != null) {
            Integer result = ddlMybatis(method, args, aClass, delete.value());
            if (result != null) {
                return result;
            }
        }
        Select select = method.getAnnotation(Select.class);
        if (select != null) {
            return dqlMybatis(method, args, aClass, select);
        }
        return null;
    }

    /**
     * @param psql
     * @param tableName
     * @param aClass
     * @param arg
     * @return
     * @Author xzj
     * @Description mybatisPlus dql 代码
     * @Date 15:45 2022/7/26
     **/
    private Object dqlMybatisPlus(String psql, String tableName, Class<? extends Type> aClass, Object arg) {
        String idName = MapperUtil.getTableIdName(aClass);
        //占位符
        StringBuilder placeholderNames = new StringBuilder();
        placeholderNames.append("and ").append(idName).append(" = ? ");
        //select * from %s where 1=1 and id = ? and name = ?
        String sql = String.format(psql, tableName, placeholderNames);
        //批量执行语句
        return jdbcTemplate.queryForObject(sql, aClass, arg);
    }

    /**
     * @param method
     * @param args
     * @param aClass
     * @param select
     * @return
     * @Author xzj
     * @Description mybatis的dql
     * @Date 10:53 2022/7/26
     **/
    private Object dqlMybatis(Method method, Object[] args, Class<? extends Type> aClass, Select select) throws InstantiationException, IllegalAccessException {
        Class<?> returnType = method.getReturnType();
        //判断args是不是一个参数 且这个参数是List<实体类> 或 实体类
        if (isEntity(aClass, args)) {
            //从方法参数上获取对应的映射关系
            List<Object> list = new ArrayList<>();
            String sql = select.value();
            List<Map<String, Object>> mapping = MapperUtil.mappingEntity(aClass, args[0]);
            if (mapping != null) {
                sql = MapperUtil.conversion(sql, list, mapping.get(0));
            }
            //如果返回类型是List集合
            if (returnType == List.class) {
                //从返回值泛型中拿返回结果
                Class<?> type = (Class) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
                return jdbcTemplate.queryForList(sql, type, list.toArray());
            }
            return jdbcTemplate.queryForObject(sql, returnType, list.toArray());
        } else {
            //如果不是entity，则是@Param参数
            List<Object> list = new ArrayList<>();
            String sql = select.value();
            Map<String, Object> mapping = MapperUtil.mappingParam(method, args);
            if (mapping != null) {
                sql = MapperUtil.conversion(sql, list, mapping);
            }
            //如果返回类型是List集合
            if (returnType.newInstance() instanceof List) {
                //从返回值泛型中拿返回结果
                Class<?> type = (Class) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
                return jdbcTemplate.queryForList(sql, type, list.toArray());
            }
            return jdbcTemplate.queryForObject(sql, returnType, list.toArray());
        }
    }

    /**
     * @param method
     * @param args
     * @param aClass
     * @param sqls
     * @return
     * @Author xzj
     * @Description mybatis 的ddl
     * @Date 10:51 2022/7/26
     **/
    private Integer ddlMybatis(Method method, Object[] args, Class<? extends Type> aClass, String[] sqls) {
        //判断args是不是一个参数 且这个参数是List<实体类> 或 实体类
        if (isEntity(aClass, args)) {
            //从方法参数上获取对应的映射关系
            List<Map<String, Object>> mapping = MapperUtil.mappingEntity(aClass, args[0]);
            if (mapping != null) {
                //多个sql
                String[] psqls = new String[sqls.length];
                //多个参数
                List<Object[]> values = new ArrayList<>();
                for (int i = 0; i < sqls.length; i++) {
                    Map<String, Object> stringObjectMap = mapping.get(i);
                    List<Object> list = new ArrayList<>();
                    psqls[i] = MapperUtil.conversion(sqls[i], list, stringObjectMap);
                    values.add(list.toArray(new Object[0]));
                }
                jdbcTemplate.update(psqls, values);
            }
        } else {
            //如果不是entity，则是@Param参数
            Map<String, Object> mapping = MapperUtil.mappingParam(method, args);
            //多个sql
            String[] psqls = new String[sqls.length];
            //多个参数
            List<Object[]> values = new ArrayList<>();

            for (int i = 0; i < sqls.length; i++) {
                List<Object> list = new ArrayList<>();
                psqls[i] = MapperUtil.conversion(sqls[i], list, mapping);
                values.add(list.toArray(new Object[0]));
            }
            return jdbcTemplate.update(psqls, values);
        }
        return null;
    }

    /**
     * @param aClass
     * @param args
     * @return
     * @Author xzj
     * @Description 判断是否是实体类或list
     * @Date 20:26 2022/7/25
     **/
    private boolean isEntity(Class<? extends Type> aClass, Object[] args) {
        return args != null && args.length == 1 &&
                (args[0] instanceof List || args[0].getClass() == aClass);
    }

    /**
     * @param psql      预编译sql
     * @param tableName 表名
     * @param aClass    类型 用来校验传入参数类型是否正确
     * @param arg       参数值
     * @return
     * @Author xzj
     * @Description 处理mybatisPlus的批量ddl操作
     * @Date 11:08 2022/7/25
     **/
    private Object ddlsMybatisPlus(String psql, String tableName, Class<? extends Type> aClass, Object arg) {
        int sum = 0;
        List<Map<String, Object>> list = MapperUtil.mappingEntity(aClass, arg);
        //保存多条sql
        String[] sqls = new String[list.size()];
        //保存多条参数
        List<Object[]> params = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            Map<String, Object> stringObjectMap = list.get(i);
            StringBuilder columnNames = new StringBuilder();
            //占位符
            StringBuilder placeholderNames = new StringBuilder();
            placeholderNames.append('(');
            //值
            List<Object> values = new ArrayList<>();
            stringObjectMap.entrySet().stream().forEach(entry -> {
                String key = entry.getKey();
                Object value = entry.getValue();
                columnNames.append(key).append(',');
                placeholderNames.append(key).append(',');
                values.add(value);
            });
            if (columnNames.length() > 1) {
                columnNames.deleteCharAt(columnNames.length() - 1);
            }
            if (placeholderNames.length() > 1) {
                placeholderNames.deleteCharAt(placeholderNames.length() - 1);
            }
            placeholderNames.append(')');
            sqls[i] = String.format(psql, tableName, columnNames, placeholderNames);
            params.add(values.toArray(new Object[0]));
        }
        //批量执行语句
        return jdbcTemplate.update(sqls, params);
    }

    /**
     * @param psql
     * @param tableName
     * @param aClass
     * @param arg
     * @return
     * @Author xzj
     * @Description 处理mybatisPlus的ddl
     * @Date 11:31 2022/7/25
     **/
    private Object ddlMybatisPlus(String psql, String tableName, Class<? extends Type> aClass, Object arg) {
        int sum = 0;
        List<Map<String, Object>> list = MapperUtil.mappingEntity(aClass, arg);
        for (Map<String, Object> stringObjectMap : list) {
            StringBuilder columnNames = new StringBuilder();
            //占位符
            StringBuilder placeholderNames = new StringBuilder();
            placeholderNames.append('(');
            //值
            List<Object> values = new ArrayList<>();
            stringObjectMap.entrySet().stream().filter(entry -> entry.getValue() != null).forEach(entry -> {
                String key = entry.getKey();
                Object value = entry.getValue();
                columnNames.append(key).append(',');
                placeholderNames.append('?').append(',');
                values.add(value);
            });
            if (columnNames.length() > 1) {
                columnNames.deleteCharAt(columnNames.length() - 1);
            }
            if (placeholderNames.length() > 1) {
                placeholderNames.deleteCharAt(placeholderNames.length() - 1);
            }
            placeholderNames.append(')');
            String sql = String.format(psql, tableName, columnNames, placeholderNames);
            sum += jdbcTemplate.update(sql, true, values.toArray());
        }
        return sum;
    }

}
